<!--
 * @Descripttion: 表单验证的input
 * @Author: armin
 * @Date: 2021-07-19 09:46:17
 * @LastEditors: armin
 * @LastEditTime: 2021-09-09 15:57:21
-->
<template>
  <div class="validate-input-contanier pb-3">
    <input
      v-if="tag !== 'textarea'"
      :class="{ 'is-invalid': inputRef.error }"
      class="form-control"
      @blur="validateInput"
      v-model="inputRef.val"
      v-bind="$attrs"
    />
    <textarea
      v-else
      :class="{ 'is-invalid': inputRef.error }"
      class="form-control"
      @blur="validateInput"
      v-model="inputRef.val"
      v-bind="$attrs"
    />
  </div>
  <span v-if="inputRef.error" class="invalid-feedback">{{
    inputRef.message
  }}</span>
</template>

<script lang="ts">
import { computed, defineComponent, onMounted, PropType, reactive } from 'vue'
import { emitter } from './ValidateForm.vue'
const emailReg =
  /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/

//! 校验规则接口定义
interface IRuleProp {
  type: 'required' | 'email' | 'range' | 'custom'
  message?: string
  min?: { length: number; message: string }
  max?: { length: number; message: string }
  validator?: () => boolean
}
export type RulesProp = IRuleProp[]
export type TagType = 'input' | 'textarea'
export default defineComponent({
  props: {
    rules: Array as PropType<RulesProp>,
    modelValue: String, // 用于实现数据双向绑定
    tag: {
      type: String as PropType<TagType>,
      default: 'input'
    }
  },
  inheritAttrs: false,
  setup (props, context) {
    // 输入框响应式对象
    const inputRef = reactive({
      val: computed({
        get: () => props.modelValue || '',
        set: val => {
          context.emit('update:modelValue', val)
        }
      }),
      error: false,
      message: ''
    })
    //! 输入框校验规则
    const validateInput = () => {
      if (props.rules) {
        const allPassed = props.rules.every((rule) => {
          let passed = true // 每个规则的临时变量
          inputRef.message = rule.message || ''
          switch (rule.type) {
            case 'required':
              passed = inputRef.val.trim() !== ''
              break
            case 'email':
              passed = emailReg.test(inputRef.val)
              break
            case 'range': {
              const { min, max } = rule
              if (min && inputRef.val.trim().length < min.length) {
                passed = false
                inputRef.message = min.message
              }
              if (max && inputRef.val.trim().length > max.length) {
                passed = false
                inputRef.message = max.message
              }
              break
            }
            case 'custom':
              passed = rule.validator ? rule.validator() : true
              break
            default:
              break
          }
          return passed
        })
        inputRef.error = !allPassed
        return allPassed
      } else {
        return true
      }
    }
    const clearInput = () => {
      inputRef.val = ''
    }
    onMounted(() => {
      emitter.emit('form-item-created', {
        validator: validateInput,
        clearInput
      })
    })
    return {
      inputRef,
      validateInput
    }
  }
})
</script>

<style>
.invalid-feedback {
  display: block;
}
</style>
